﻿using Modbus.Data;
using Modbus.Device;
using Slave.Properties;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Slave
{
    public partial class Main : Form
    {
        #region Filed
        /// <summary>
        /// Array of serilaBaud 
        /// </summary>
        private int[] serialBaud = { 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 230400, 25600 };
        /// <summary>
        /// Array of serialDataBit
        /// </summary>
        private int[] serialDataBit = { 5, 6, 7, 8 };
        /// <summary>
        /// Array of serialStopBit for comboBox
        /// </summary>
        private double[] serialStopBitCbb = { 1, 1.5, 2 };
        /// <summary>
        /// Array of serialStopBit
        /// </summary>
        private StopBits[] serialStopBit = { StopBits.One, StopBits.OnePointFive, StopBits.Two };
        /// <summary>
        /// Array of serialParity for comboBox
        /// </summary>
        private string[] serialParityCbb = { "None Parity", "Odd Parity", "Even Parity" };
        /// <summary>
        /// Array of serialParity
        /// </summary>
        private Parity[] serialParity = { Parity.None, Parity.Odd, Parity.Even };
        /// <summary>
        /// SerialPort instance
        /// </summary>
        private SerialPort m_serialPort = null;
        /// <summary>
        /// Connect state of transport
        /// </summary>
        private bool isTransPortConnect = false;
        /// <summary>
        /// Array of serialPortName
        /// </summary>
        private string[] serialPortName = null;

        // 实时监控页面  线圈控件list
        private List<PictureBox> coilPicList = new List<PictureBox>();

        // 实时监控页面  离散输入控件list
        private List<PictureBox> discreteInputList = new List<PictureBox>();

        // 实时监控页面  脉冲计数存储控件List
        private List<NumberBox> holdingRegisterList = new List<NumberBox>();

        // 实时监控页面  模拟输入存储控件List
        private List<NumberBox> inputRegisterList = new List<NumberBox>();
        #endregion

        private ModbusSerialSlave m_modbusSlave = null;
        private DataStore m_DataStore;

        public Main()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;
        }


        private void Main_Load(object sender, EventArgs e)
        {
            // load serial port name 
            Dictionary<string, string> dir = HardwareHelper.MulGetHardwareGetCom();

            m_serialPort = new SerialPort();

            if (dir != null)
            {
                serialPort_cbb.Items.Clear();

                foreach (var value in dir.Values)
                {
                    serialPort_cbb.Items.Add(value);
                }

                serialPortName = dir.Keys.ToArray();
                serialPort_cbb.SelectedIndex = 0;
            }

            // load information of serial 
            foreach (int baud in serialBaud)
            {
                serialBaud_cbb.Items.Add(baud + " Baud");
            }
            serialBaud_cbb.SelectedIndex = 11;

            foreach (int dataBit in serialDataBit)
            {
                serialDatabit_cbb.Items.Add(dataBit + " Data Bits");
            }
            serialDatabit_cbb.SelectedIndex = 3;

            foreach (string parity in serialParityCbb)
            {
                serialParity_cbb.Items.Add(parity);
            }
            serialParity_cbb.SelectedIndex = 0;

            foreach (double stopBit in serialStopBitCbb)
            {
                serialStopbit_cbb.Items.Add(stopBit + " Stop Bit");
            }
            serialStopbit_cbb.SelectedIndex = 0;

            // 布局空间
            // 线圈布局
            AddCoilControls(Coli_gbx);
            // 离散输入布局
            AddInputControls(Input_gbx);
            // 保存寄存器布局
            AddHoldingRegisterControls(HoldingRegister_gbx);
            // 输入寄存器布局
            AddInputRegisterControls(InputRegister_gbx);

        }

        /// <summary>
        /// 添加离散线圈控件
        /// </summary>
        /// <param name="gb"></param>
        private void AddCoilControls(GroupBox gb)
        {

            for (int i = 0; i < 32; i++)
            {
                // 添加 图片
                PictureBox pb = new PictureBox();
                pb.Name = "DO_" + i;
                pb.Size = new Size(25, 25);
                pb.SizeMode = PictureBoxSizeMode.StretchImage;
                pb.Image = Resources.red;
                // 布局关键是位置
                // 第一行：第一个(6,25),第二个(52,25)...
                // 第二行: 第一个(6,75),第二个(52,75)...
                pb.Location = new Point(6 + (i % 16) * 46, 25 + (i / 16) * 55);
                // tag 作为参数识别控件
                pb.Tag = i;
                pb.Click += (sender, e) => CoilPicture_Click(pb, e);
                coilPicList.Add(pb);
                gb.Controls.Add(pb);

                // 文本标记
                Label lbl = new Label();
                lbl.Name = "DO" + (i + 1) + "_lbl";
                lbl.Text = "DO" + (i + 1);
                lbl.AutoSize = true;
                // 位置信息
                lbl.Location = new Point(6 + (i % 16) * 46, 55 + (i / 16) * 55);
                gb.Controls.Add(lbl);
            }
        }

        /// <summary>
        /// 离散线圈控件--数据处理
        /// </summary>
        /// <param name="pb"></param>
        /// <param name="e"></param>
        private void CoilPicture_Click(PictureBox pb, EventArgs e)
        {
            if (!m_serialPort.IsOpen)
            {
                MessageBox.Show("请先启动服务");
                return;
            }
            // 或取tag参数
            int index = (int)pb.Tag;
            Console.WriteLine(index.ToString());
            // 通过tag的参数关联数据集合，红色是false 绿是true
            if (!m_DataStore.CoilDiscretes[index + 1])
            {
                m_DataStore.CoilDiscretes[index + 1] = true;
                pb.Image = Resources.green;
            }
            else
            {
                m_DataStore.CoilDiscretes[index + 1] = false;
                pb.Image = Resources.red;
            }
        }

        /// <summary>
        /// 添加离散输入控件
        /// </summary>
        /// <param name="gb"></param>
        private void AddInputControls(GroupBox gb)
        {
            for (int i = 0; i < 32; i++)
            {
                PictureBox pb = new PictureBox();
                pb.Name = "DI" + i + "_pb";
                pb.Size = new Size(25, 25);
                pb.SizeMode = PictureBoxSizeMode.StretchImage;
                pb.Image = Properties.Resources.red;
                pb.Location = new Point(6 + (i % 16) * 46, 25 + (i / 16) * 55);
                pb.Tag = i;
                pb.Click += (sender, e) => InputPicture_Click(pb, e);
                discreteInputList.Add(pb);
                gb.Controls.Add(pb);


                Label lbl = new Label();
                lbl.Name = "DI" + (i + 1) + "_lbl";
                lbl.Text = "DI" + (i + 1);
                lbl.AutoSize = true;
                lbl.Location = new Point(6 + (i % 16) * 46, 55 + (i / 16) * 55);
                gb.Controls.Add(lbl);
            }
        }

        /// <summary>
        /// 离散输入控件--数据处理
        /// </summary>
        /// <param name="pb"></param>
        /// <param name="e"></param>
        private void InputPicture_Click(PictureBox pb, EventArgs e)
        {
            if (!m_serialPort.IsOpen)
            {
                MessageBox.Show("请先启动服务");
            }
            int index = (int)pb.Tag;
            Console.WriteLine(index.ToString());
            if (!m_DataStore.InputDiscretes[index + 1])
            {
                m_DataStore.InputDiscretes[index + 1] = true;
                pb.Image = Resources.green;
            }
            else
            {
                m_DataStore.InputDiscretes[index + 1] = false;
                pb.Image = Resources.red;
            }
        }

        /// <summary>
        /// 添加保持寄存器控件
        /// </summary>
        /// <param name="gb"></param>
        private void AddHoldingRegisterControls(GroupBox gb)
        {
            for (int i = 0; i < 16; i++)
            {
                Label lbl = new Label();
                lbl.Name = "HR" + (i + 1) + "_lbl";
                lbl.Text = "HR" + (i + 1);
                lbl.Location = new Point(15 + (i % 8) * 90, 20 + (i / 8) * 35 + 3);
                lbl.AutoSize = true;
                gb.Controls.Add(lbl);

                NumberBox nbb = new NumberBox();
                nbb.Name = "HR_htb_" + i;
                nbb.Text = "0";
                nbb.Tag = i;
                nbb.TextChanged += new EventHandler(holding_txb_TextChanged);
                nbb.Location = new Point(45 + (i % 8) * 90, 20 + (i / 8) * 35);
                nbb.Size = new Size(50, 20);
                // nbb.Enabled = false;
                holdingRegisterList.Add(nbb);
                gb.Controls.Add(nbb);
            }
        }

        /// <summary>
        /// 保持寄存器控件--数据处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void holding_txb_TextChanged(object sender, EventArgs e)
        {
            TextBox txb = (TextBox)sender;
            if (!m_serialPort.IsOpen)
            {
                MessageBox.Show("请先启动服务");
                txb.Text = "0";
                return;
            }

            int index = (int)txb.Tag;
            Console.WriteLine(index.ToString());
            m_DataStore.HoldingRegisters[index + 1] = Convert.ToUInt16(txb.Text);
        }

        /// <summary>
        /// 添加输入寄存器控件
        /// </summary>
        /// <param name="gb"></param>
        private void AddInputRegisterControls(GroupBox gb)
        {
            for (int i = 0; i < 16; i++)
            {
                Label lbl = new Label();
                lbl.Name = "IR" + (i + 1) + "_lbl";
                lbl.Text = "IR" + (i + 1);
                lbl.Location = new Point(15 + (i % 8) * 90, 28 + (i / 8) * 35 + 3);
                lbl.AutoSize = true;
                gb.Controls.Add(lbl);

                NumberBox nbb = new NumberBox();
                nbb.Name = "IR_nbb_" + i;
                nbb.Text = "0";
                nbb.Tag = i;
                nbb.TextChanged += new EventHandler(input_txb_TextChanged);
                nbb.Location = new Point(45 + (i % 8) * 90, 28 + (i / 8) * 35);
                nbb.Size = new Size(50, 20);
                // nbb.Enabled = false;
                inputRegisterList.Add(nbb);
                gb.Controls.Add(nbb);
            }
        }

        /// <summary>
        /// 输入寄存器控件--数据处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void input_txb_TextChanged(object sender, EventArgs e)
        {
            TextBox txb = (TextBox)sender;
            if (!m_serialPort.IsOpen)
            {
                MessageBox.Show("请先启动服务");
                txb.Text = "0";
                return;
            }

            int index = (int)txb.Tag;
            Console.WriteLine(index.ToString());
            m_DataStore.InputRegisters[index + 1] = Convert.ToUInt16(txb.Text);
        }

        #region 其他
        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start("https://space.bilibili.com/304463098");
        }

        private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start("https://gitee.com/electronic-diy-studio");
        }

        #endregion


        /// <summary>
        /// 启动和停止服务
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void connect_btn_Click(object sender, EventArgs e)
        {
            // 如果串口是打开的，就关闭slave服务
            if (m_serialPort.IsOpen)
            {
                try
                {
                    connect_btn.Text = "启动";
                    m_serialPort.Close();
                    m_modbusSlave.Dispose();
                }
                catch (Exception)
                {
                    // handle error
                    throw;
                }
            }
            // 如果串口是关闭的，就启动slave服务
            else
            {
                try
                {
                    connect_btn.Text = "停止";
                    // load params
                    m_serialPort.PortName = serialPortName[serialPort_cbb.SelectedIndex];
                    m_serialPort.BaudRate = serialBaud[serialBaud_cbb.SelectedIndex];
                    m_serialPort.DataBits = serialDataBit[serialDatabit_cbb.SelectedIndex];
                    m_serialPort.StopBits = serialStopBit[serialStopbit_cbb.SelectedIndex];
                    m_serialPort.Parity = serialParity[serialParity_cbb.SelectedIndex];

                    // open serial port
                    m_serialPort.Open();

                    // create the slave 0x01 is sation
                    m_modbusSlave = ModbusSerialSlave.CreateRtu(0x01, m_serialPort);

                    // create the datastore
                    m_modbusSlave.DataStore = DataStoreFactory.CreateDefaultDataStore(32, 32, 16, 16);

                    //m_modbusSlave.DataStore.DataStoreReadFrom +=new EventHandler<DataStoreEventArgs>(Handle_read_data);
                    m_modbusSlave.DataStore.DataStoreWrittenTo += new EventHandler<DataStoreEventArgs>(Handle_wirte_data);

                    m_DataStore = m_modbusSlave.DataStore;


                    // start listen
                    Task.Factory.StartNew(new Action(() =>
                    {
                        m_modbusSlave.Listen();
                    }));

                }
                catch (Exception)
                {
                    // handle error
                    throw;
                }
            }
        }

        /// <summary>
        /// 处理写入的数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Handle_wirte_data(object sender, DataStoreEventArgs e)
        {
            Console.WriteLine("Handle_wirte_data");
            switch (e.ModbusDataType)
            {
                case ModbusDataType.HoldingRegister:
                    {
                        update_holdingRegister();
                    }
                    break;
                case ModbusDataType.InputRegister:
                    {
                  //      update_inputRegister();
                    }
                    break;
                case ModbusDataType.Coil:
                    {
                        update_coil();
                    }
                    break;
                case ModbusDataType.Input:
                    {
                    //    update_input();
                    }
                    break;
                default:
                    break;
            }


        }

        private void update_input()
        {
            throw new NotImplementedException();
        }
        
        /// <summary>
        /// 
        /// </summary>
        private void update_coil()
        {
            try
            {
                for (int i = 0; i < coilPicList.Count; i++)
                {
                 //   Console.WriteLine(m_DataStore.HoldingRegisters[i + 1].ToString());
                    if(m_DataStore.CoilDiscretes[i+1])
                    {
                        coilPicList[i].Image = Resources.green;
                    }
                    else
                    {
                        coilPicList[i].Image = Resources.red;
                    }
                   
                }
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.Message.ToString());
            }
        }

        /// <summary>
        /// 更新输入寄存器到UI
        /// </summary>
        private void update_inputRegister()
        {

        }

        /// <summary>
        ///  更新保持寄存器数据到UI
        /// </summary>
        private void update_holdingRegister()
        {
            try
            {
                for (int i = 0; i < holdingRegisterList.Count; i++)
                {
                    Console.WriteLine(m_DataStore.HoldingRegisters[i + 1].ToString());
                    holdingRegisterList[i].Text = m_DataStore.HoldingRegisters[i + 1].ToString();
                }
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.Message.ToString());
            }
        }
        /// <summary>
        /// 读取数据的处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Handle_read_data(object sender, EventArgs e)
        {
            Console.WriteLine("Handle_read_data");
        }


    }
}
